//
//  ViewController.m
//  多线程
//
//  Created by hfzhangzhang on 2018/4/9.
//  Copyright © 2018年 hfzhangzhang. All rights reserved.
//

#import "ViewController.h"
#import <pthread.h>

@interface ViewController ()
/** 票 */
@property(assign,nonatomic)int tickets;
/** 锁  */
@property(nonatomic,strong)NSObject * lockObjc;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    //串行异步执行-->顺序执行,开线程，会开2条线程
    dispatch_queue_t q1 = dispatch_queue_create("zhangzhang1", NULL);
    for (int i = 0; i < 100; i++) {

         NSLog(@"--%@--%d--",[NSThread currentThread],i);
        dispatch_async(q1, ^{
            NSLog(@"%@  %d",[NSThread currentThread],i);
        });
    }
    NSLog(@"come here");
    
//    NSString *name = [[NSString alloc]initWithString:@"张三"];
//    NSLog(@"%d",[name reatinCount]);
    
//    //number == 1 说明是主线程  != 1 就是其他线程
//    NSLog(@"%@",[NSThread currentThread]);
//     self.lockObjc = [[NSObject alloc]init];
}


-(void)pthreadDemo{
    
    /**
     pthread 是属于 POSIX 多线程开发框架
     
     参数:
     1.指向线程代号的指针
     2.线程的属性
     3.指向函数的指针
     4.传递给该函数的参数
     
     返回值
     - 如果是0,标示正确
     - 如果非0,标示错误代码
     
     void *   (*)      (void *)
     返回值   (函数指针)  (参数)
     void *  和OC中的  id 是等价的!
     
     
     */
    NSString * str = @"hello Hank";
    pthread_t threadId;
    /**
     - 在 ARC 开发中,如果涉及到和C语言中的相同的数据类型进行转换,需要使用 __bridge "桥接"
     - 在 MRC 不需要
     */
    
    int result = pthread_create(&threadId, NULL, &demo, (__bridge  void *)(str));
    
    if (result == 0) {
        NSLog(@"OK");
    }else{
        NSLog(@"error %d",result);
    }
    
    
    
}

void * demo(void * param){
    NSLog(@"%@ %@",[NSThread currentThread],param);
    
    return NULL;
}

-(void)threadDemo{
    
    NSLog(@"A-------------");
    
    // 创建一个NSThread
    NSThread * thread = [[NSThread alloc]initWithTarget:self selector:@selector(demo:) object:@"Thread"];
    
    thread.name = @"Thread A";
    //优先级 从 0.0 -- 1.0  默认值 0.5
    /**  优先级翻转
     优先级 只是保证 CPU 调度的可能性会高!
     
     多线程目的:将耗时操作放在后台,不阻塞UI线程!
     建议:在开发的时候,不要修改优先级
     
     在多线程开发中,不要相信一次的运行结果!!
     */
    thread.threadPriority = 0.1;

    
    //启动线程
    [thread start];
    for (int i = 0; i<10; i++) {
        NSLog(@"%d",i);
    }
    NSLog(@"B-------------");
    
    [NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@"Detach"];
    NSLog(@"C-------------");
    
    //InBackground 就是在后台(子线程)运行!!
    [self performSelectorInBackground:@selector(demo:) withObject:@"background"];
    //1
}



-(void)demo:(id)obj{
    for (int i = 0; i < 2; i++) {
        //!=1
        NSLog(@"C---------------%@",[NSThread currentThread]);
    }
    //提示:sleep 方法是类方法,会直接休眠当前线程!!
     //[NSThread sleepForTimeInterval:2.0];
    //注意!!! exit会杀掉主线程!但是APP不会挂掉!!
   // [NSThread exit];
}

-(void)saleTickets{
    while (YES) {
        //0.买票要1秒钟
        [NSThread sleepForTimeInterval:1.0];
        //参数:就是能够加锁的任意 NSOjbect 对象
        //局部变量: 每个线程单独拥有的,无法锁住!!
        //注意: 锁一定要是所有线程共享的对象!!,一般用self
        //NSObject * lockObj = [[NSObject alloc]init];
        @synchronized (self.lockObjc) {
            //互斥锁 - 保证锁内的代码,同一时间,只有一条线程能够执行!!
            //互斥锁它的范围,应该尽量小,锁范围越大,效率越低!
            //1.还有没有票
            if (self.tickets > 0) {
                //2.卖出一张票
                self.tickets --;
                NSLog(@"剩下 %d 张票 --- %@",self.tickets,[NSThread currentThread]);
            }else{
                NSLog(@"没有票了 %@",[NSThread currentThread]);
                break;
            }
        }
    }
    
}

/**
 什么是进程进程是指在系统中正在运行的一个应用程序,每个进程之间是独立的，每个进程均运行在其专用且受保护的内存空间内

 1个线程中任务的执行是串行的
 如果要在1个线程中执行多个任务，那么只能一个一个地按顺序执行这些任务
 也就是说，在同一时间内，1个线程只能执行1个任务
 
 什么是多线程
 1个进程中可以开启多条线程，每条线程可以并行（同时）执行不同的任务,多线程技术可以提高程序的执行效率

 多线程的原理
 同一时间，CPU只能处理1条线程，只有1条线程在工作（执行）
 多线程并发（同时）执行，其实是CPU快速地在多条线程之间调度（切换）
 如果CPU调度线程的时间足够快，就造成了多线程并发执行的假象
 思考：如果线程非常非常多，会发生什么情况？
 CPU会在N多线程之间调度，CPU会累死，消耗大量的CPU资源
 每条线程被调度执行的频次会降低（线程的执行效率降低）

 多线程的优点
 能适当提高程序的执行效率
 能适当提高资源利用率（CPU、内存利用率）
 
 多线程的缺点
 创建线程是有开销的，iOS下主要成本包括：内核数据结构（大约1KB）、栈空间（子线程512KB、主线程1MB，也可以使用-setStackSize:设置，但必须是4K的倍数，而且最小是16K），创建线程大约需要90毫秒的创建时间
 如果开启大量的线程，会降低程序的性能
 线程越多，CPU在调度线程上的开销就越大
 程序设计更加复杂：比如线程之间的通信、多线程的数据共享
 
 1块资源可能会被多个线程共享，也就是多个线程可能会访问同一块资源
 比如多个线程访问同一个对象、同一个变量、同一个文件
 当多个线程访问同一块资源时，很容易引发数据错乱和数据安全问题

 
 什么是主线程
 一个iOS程序运行后，默认会开启1条线程，称为“主线程”或“UI线程”
 
 主线程的主要作用
 显示\刷新UI界面
 处理UI事件（比如点击事件、滚动事件、拖拽事件等）
 共同一个约定,所有更新UI的操作都在主线程上执行!原因:几乎所有的UIKit 都不是线程安全的!"取舍"为了性能
 //提示: 不是所有的更新UI 在后台线程执行都会有问题!
 //重点提示: !! 不要在子线程去做更新UI的操作!!
 
 主线程的使用注意
 别将比较耗时的操作放到主线程中
 耗时操作会卡住主线程，严重影响UI的流畅度，给用户一种“卡”的坏体验
 
 pthread
 一套通用的多线程API,适用于Unix\Linux\Windows等系统,跨平台\可移植,使用难度大  C语言  线程生命周期程序员管理  几乎不用

 NSThread
 使用更加面向对象 简单易用，可直接操作线程对象  OC语言  线程生命周期程序员管理  偶尔使用
 
 GCD
 旨在替代NSThread等线程技术，充分利用设备的多核  C语言  线程生命周期自动管理  经常使用

 NSOperation
 基于GCD（底层是GCD） 比GCD多了一些更简单实用的功能，使用更加面向对象 OC   线程生命周期自动管理  经常使用




 
 OC在定义属性时有nonatomic和atomic两种选择
 atomic：原子属性，为setter方法加锁（默认就是atomic），线程安全，需要消耗大量的资源,单(线程)写多(线程)读线程技术,同样有可能出现"脏数据",重新读一下
 原子属性内部就有一把自旋锁
 
 自旋锁 & 互斥锁
 - 共同点
 都能保证同一个时间点就只有一条线程访问!
 - 不同点
 互斥锁:如果发现有其他线程正在执行锁定的代码,线程就会进入睡眠状态,等待其他线程执行完毕,打开锁之后,线程就会被唤醒!
 自旋锁:如果发现有其他线程正在执行锁定的代码,线程就会用死循环的方式,一直等待锁定代码执行完毕!
 自旋锁更适合执行非常短的代码!
 
 无论什么锁,都是以"性能"作为代价来保证"安全",应该尽量小,锁范围越大,效率越低!
 
 nonatomic：非原子属性，不会为setter方法加锁，非线程安全，适合内存小的移动设备
 iOS开发的建议
 所有属性都声明为nonatomic
 尽量避免多线程抢夺同一块资源
 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理，减小移动客户端的压力
 
 
 什么叫做线程间通信
 在1个进程中，线程往往不是孤立存在的，多个线程之间需要经常进行通信
 
 线程间通信的体现
 1个线程传递数据给另1个线程
 在1个线程中执行完特定任务后，转到另1个线程继续执行任务
 
 线程间通信常用方法
 - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
 - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;


 */

/**
 OC 中定义属性,通常会声明一个  _成员变量 (这个功能是Xcode 的功能!)
 如果你同时重写了属性的 getter & setter 方法, _成员变量,就不会自动生成!
 @synthesize 合成指令, 在Xcode 4.5 之前用得非常多!Xcode 4.5 之前属性不会自动生成 _成员变量
 */

@end
